{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**导入模块**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# https://github.com/lhandal/audioscrobbler_music_recommender_pyspark/blob/master/Music_Recommender_Leandro_Handal.ipynb"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib as plt\n",
    "import seaborn as sns\n",
    "import matplotlib.pylab as plt\n",
    "%matplotlib inline\n",
    "import warnings\n",
    "warnings.filterwarnings('ignore')\n",
    "from ipywidgets import interact, interactive, fixed, interact_manual\n",
    "import ipywidgets as widgets\n",
    "from pyspark.sql.types import StructField, StringType, IntegerType, StructType, ByteType"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 一、项目简介\n",
    "#### 1.1 项目背景\n",
    "    本 章 示 例 使 用 Audioscrobbler 公 开 发 布 的 一 个 数 据 集。 Audioscrobbler 是 last.fm 的 第一个音乐推荐系统。 last.fm 创建于 2002 年，是最早的互联网流媒体广播站点之一。\n",
    "\n",
    "    Audioscrobbler 数据集有些特别， 因为它只记录了播放数据，主要的数据集在文件 user_artist_data.txt 中，它包含 141 000 个用户和 160 万个艺术家，记录了约 2420 万条用户播放艺术家歌曲的信息，其中包括播放次数信息。\n",
    "#### 1.2 数据集介绍\n",
    "    数据集主要包含以下三个文件：\n",
    "+ user_artist_data.txt\n",
    "+ artist_data.txt\n",
    "+ artist_alias.txt\n",
    "\n",
    "#### 1.3 数据集字段说明\n",
    "\n",
    "**user_artist_data**\n",
    "   + userid：用户id\n",
    "   + artistid：艺术家id\n",
    "   + playcount：播放次数\n",
    "   \n",
    "**artist_data.txt**\n",
    "   + artistid：艺术家id\n",
    "   + artist_name：艺术家名称\n",
    "\n",
    "**artist_alias.txt**\n",
    "   + badid 错误id\n",
    "   + goodid 正确id\n",
    "\n",
    "    数据集在 artist_data.txt 文件中给出了每个艺术家的 ID 和对应的名字。请注意，记录播放信息时，客户端应用提交的是艺术家的名字。名字如果有拼写错误，或使用了非标准的名称， 事后才能被发现。 比如，“The Smiths”“Smiths, The”和“the smiths”看似代表不同艺术家的 ID，但它们其实明显是指同一个艺术家。因此，为了将拼写错误的艺术家 ID 或ID 变体对应到该艺术家的规范 ID，数据集提供了 artist_alias.txt 文件。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 二、准备数据集\n",
    "    注意：在开始实验之前，请确保已经将上述三个训练数据集上传至hadoop hdfs文件系统中"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.1 加载数据集\n",
    "    以下代码将从hadoop hdfs文件系统中加载训可用的三个训练数据集，分别是user_artist_data.txt、\n",
    "    artist_data.txt和artist_alias.txt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 以local模式创建SparkSession\n",
    "from pyspark.sql import SparkSession\n",
    "spark = SparkSession.builder.getOrCreate()\n",
    "spark.conf.set(\"spark.sql.shuffle.partitions\",\"5\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = spark.read.csv('hdfs://172.17.0.2:9000/musicRecommand/user_artist_data.txt', \n",
    "                      sep=' ',\n",
    "                      inferSchema = True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "customSchemaArtists = StructType([ \\\n",
    "    StructField(\"artistid\", IntegerType(), True), \\\n",
    "    StructField(\"name\", StringType(), True)])\n",
    "\n",
    "artists = spark.read.csv('hdfs://172.17.0.2:9000/musicRecommand/artist_data.txt', \n",
    "                         sep='\\t', \n",
    "                         schema = customSchemaArtists)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "aliases = spark.read.csv('hdfs://172.17.0.2:9000/musicRecommand/artist_alias.txt', \n",
    "                         sep='\\t', \n",
    "                         inferSchema=True)\n",
    "# 对数据列名称进行重命名\n",
    "aliases = aliases.withColumnRenamed(\"_c0\", \"mispelledid\").withColumnRenamed(\"_c1\", \"artistid\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 对数据列名称进行重命名\n",
    "data = data.withColumnRenamed(\"_c0\", \"userid\"). \\\n",
    "            withColumnRenamed(\"_c1\", \"artistid\"). \\\n",
    "            withColumnRenamed(\"_c2\", \"plays\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+-------+------------------+------------------+-----------------+\n",
      "|summary|            userid|          artistid|            plays|\n",
      "+-------+------------------+------------------+-----------------+\n",
      "|  count|          24296858|          24296858|         24296858|\n",
      "|   mean|1947573.2653533637|1718704.0937568964|15.29576248089362|\n",
      "| stddev| 496000.5551819132| 2539389.092428355|153.9153244697963|\n",
      "|    min|                90|                 1|                1|\n",
      "|    max|           2443548|          10794401|           439771|\n",
      "+-------+------------------+------------------+-----------------+\n",
      "\n"
     ]
    }
   ],
   "source": [
    "data.describe().show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.2 移除数据集中的异常值\n",
    "+ 为了构建系统过滤推荐系统，我们需要得到用户喜好矩阵。因此，为了让构建的推荐系统性能更好，我们需要剔除掉播放次数小于5的用户数据\n",
    "+ 同理，为了不影响模型性能，我们也需要将播放次数超过一定阈值的用户数据删除，此处我们设置阈值为1000"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+-------+------------------+------------------+-----------------+\n",
      "|summary|            userid|          artistid|            plays|\n",
      "+-------+------------------+------------------+-----------------+\n",
      "|  count|           9307579|           9307579|          9307579|\n",
      "|   mean| 1939013.336777265|1319484.9944439903|33.86969006655759|\n",
      "| stddev|490854.13913767406| 2015181.414814242|66.60665066398262|\n",
      "|    min|                90|                 1|                5|\n",
      "|    max|           2443507|          10794325|             1000|\n",
      "+-------+------------------+------------------+-----------------+\n",
      "\n"
     ]
    }
   ],
   "source": [
    "subset = data.filter((data['plays'] >= 5) & (data['plays'] <= 1000))\n",
    "subset.describe().show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+-------+--------+-----+\n",
      "| userid|artistid|plays|\n",
      "+-------+--------+-----+\n",
      "|1000002|       1|   55|\n",
      "|1000002| 1000006|   33|\n",
      "|1000002| 1000007|    8|\n",
      "|1000002| 1000009|  144|\n",
      "|1000002| 1000010|  314|\n",
      "|1000002| 1000013|    8|\n",
      "|1000002| 1000014|   42|\n",
      "|1000002| 1000017|   69|\n",
      "|1000002| 1000024|  329|\n",
      "|1000002| 1000028|   17|\n",
      "|1000002| 1000031|   47|\n",
      "|1000002| 1000033|   15|\n",
      "|1000002| 1000055|   25|\n",
      "|1000002| 1000062|   71|\n",
      "|1000002| 1000088|  157|\n",
      "|1000002| 1000099|   57|\n",
      "|1000002| 1000113|   30|\n",
      "|1000002| 1000123|   27|\n",
      "|1000002| 1000127|   53|\n",
      "|1000002| 1000139|   56|\n",
      "+-------+--------+-----+\n",
      "only showing top 20 rows\n",
      "\n"
     ]
    }
   ],
   "source": [
    "subset.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.2 数据分布直方图\n",
    "    以下代码中，我们通过频率分布直方图，查看user_artist_data.txt数据中plays变量的分布情况"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 将数据集转化为pandas DataFrame\n",
    "subset_pd = subset.sample(withReplacement=False, \n",
    "                          fraction=0.01, \n",
    "                          seed=42).toPandas()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, 'Play Count Distribution')"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1080x432 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 使用matplotlib绘制plays频率分布直方图\n",
    "plt.subplots(figsize=(15, 6))\n",
    "sns.distplot(subset_pd.plays)\n",
    "plt.grid(which=\"major\", axis=\"y\")\n",
    "plt.xlabel(\"Play Count\", size=14)\n",
    "plt.title(\"Play Count Distribution\", size=16)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.3 检查数空值\n",
    "    以下代码将检查数据集的任意数据列中是否存在空值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pyspark.sql.functions import isnan, when, count, col, log1p, expm1, instr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+------+--------+-----+\n",
      "|userid|artistid|plays|\n",
      "+------+--------+-----+\n",
      "|     0|       0|    0|\n",
      "+------+--------+-----+\n",
      "\n"
     ]
    }
   ],
   "source": [
    "subset.select([count(when(isnan(c), c)).alias(c) for c in data.columns]).show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "root\n",
      " |-- userid: integer (nullable = true)\n",
      " |-- artistid: integer (nullable = true)\n",
      " |-- plays: integer (nullable = true)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 输出数据集字段信息\n",
    "subset.printSchema()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.4 组合数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+--------+--------------------+\n",
      "|artistid|                name|\n",
      "+--------+--------------------+\n",
      "| 1134999|        06Crazy Life|\n",
      "| 6821360|        Pang Nakarin|\n",
      "|10113088|Terfel, Bartoli- ...|\n",
      "|10151459| The Flaming Sidebur|\n",
      "| 6826647|   Bodenstandig 3000|\n",
      "|10186265|Jota Quest e Ivet...|\n",
      "| 6828986|       Toto_XX (1977|\n",
      "|10236364|         U.S Bombs -|\n",
      "| 1135000|artist formaly kn...|\n",
      "|10299728|Kassierer - Musik...|\n",
      "|10299744|         Rahzel, RZA|\n",
      "| 6864258|      Jon Richardson|\n",
      "| 6878791|Young Fresh Fello...|\n",
      "|10299751|          Ki-ya-Kiss|\n",
      "| 6909716|Underminded - The...|\n",
      "|10435121|             Kox-Box|\n",
      "| 6918061|  alexisonfire [wo!]|\n",
      "| 1135001|         dj salinger|\n",
      "| 6940391|The B52's - Chann...|\n",
      "|10475396|             44 Hoes|\n",
      "+--------+--------------------+\n",
      "only showing top 20 rows\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 查看艺术家id和艺术家名称\n",
    "artists.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+-------+-----------------+---------------+\n",
      "|summary|         artistid|           name|\n",
      "+-------+-----------------+---------------+\n",
      "|  count|          1848281|        1848282|\n",
      "|   mean|7028265.171776369|       Infinity|\n",
      "| stddev| 3753055.54540281|            NaN|\n",
      "|    min|                1|              \u0001|\n",
      "|    max|         10794305|￿￿￿￿￿￿￿￿￿￿￿￿くȁ|\n",
      "+-------+-----------------+---------------+\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 查看artis数据集统计信息\n",
    "artists.describe().show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 将artists数据集注册到临时表\n",
    "artists.createOrReplaceTempView(\"artists\")\n",
    "# 查询艺术家名称中包含“Aerosmith”的所有艺术家\n",
    "sqlDF = spark.sql(\"SELECT * FROM artists where name like 'Aerosmith%'\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+--------+--------------------+\n",
      "|artistid|                name|\n",
      "+--------+--------------------+\n",
      "| 6946007|    Aerosmith/RunDMC|\n",
      "|10475683|Aerosmith: Just P...|\n",
      "| 1083031|    Aerosmith/ G n R|\n",
      "| 2027746|Aerosmith by Musi...|\n",
      "|10237208| Aerosmith + Run DMC|\n",
      "|10588537|Aerosmith, Kid Ro...|\n",
      "| 9934757|Aerosmith - Big Ones|\n",
      "|10479781|      Aerosmith Hits|\n",
      "| 7032554|Aerosmith & Run-D...|\n",
      "|10033592|          Aerosmith?|\n",
      "|10594066|Aerosmith - JPP - 07|\n",
      "|10594406|Aerosmith - JPP - 04|\n",
      "|10482681|Aerosmith - O, Ye...|\n",
      "|10033746| Aerosmith/Aerosmith|\n",
      "| 1267101|Aerosmith With Bo...|\n",
      "| 1269416|  Aerosmith & RunDmc|\n",
      "|10441202|Aerosmith With Le...|\n",
      "|10239445|   Aerosmith - [EMG]|\n",
      "| 1157432|Aerosmith (Pandor...|\n",
      "| 9935381|Aerosmith - Nine ...|\n",
      "+--------+--------------------+\n",
      "only showing top 20 rows\n",
      "\n"
     ]
    }
   ],
   "source": [
    "sqlDF.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+--------+---------+\n",
      "|artistid|     name|\n",
      "+--------+---------+\n",
      "| 1000010|Aerosmith|\n",
      "+--------+---------+\n",
      "\n",
      "+--------+------------+\n",
      "|artistid|        name|\n",
      "+--------+------------+\n",
      "| 2082323|01 Aerosmith|\n",
      "+--------+------------+\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 查看艺术家id分别为1000010和2082323所对应的艺术家名称\n",
    "artists[artists.artistid==1000010].show()\n",
    "artists[artists.artistid==2082323].show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+-----------+--------+\n",
      "|mispelledid|artistid|\n",
      "+-----------+--------+\n",
      "+-----------+--------+\n",
      "\n",
      "+-----------+--------+\n",
      "|mispelledid|artistid|\n",
      "+-----------+--------+\n",
      "|    2082323| 1000010|\n",
      "+-----------+--------+\n",
      "\n"
     ]
    }
   ],
   "source": [
    "aliases[aliases.mispelledid==1000010].show()\n",
    "aliases[aliases.mispelledid==2082323].show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通过以上代码分析发现，同一个艺术有可能存在多个id，例如以上名为“Aerosmith”的艺术家，有1000010和2082323与之对应\n",
    "接下来，我们编写函数，检查某个艺术家名称是否对应多个不通的id"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "def is_standard(id1):\n",
    "    \"\"\"Return True is the ID is not in the mispelledID column of artistAliasDF\"\"\"\n",
    "    if aliases[aliases.mispelledid==id1].collect()==[]:\n",
    "        return True\n",
    "    else:\n",
    "        return False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+--------+\n",
      "|artistid|\n",
      "+--------+\n",
      "|10586963|\n",
      "+--------+\n",
      "\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "name1 = \"Green Day - Oasis - Eminem - Aerosmith\"\n",
    "id1 = artists[artists.name==name1].select('artistid').show(1)\n",
    "is_standard(id1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**组合数据集**\n",
    "\n",
    "    接下来，我们将user_artist_data.txt数据集和artist_data数据集进行组合，查看每个artistid\n",
    "    所对应的艺术家名称"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "subset[subset.artistid==id1].collect()\n",
    "strangeuserid=subset[subset.artistid==id1].userid\n",
    "strangeUserActivity = subset[subset.userid==strangeuserid]\n",
    "artists = strangeUserActivity.join(artists, on=\"artistid\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+--------+-------+-----+---------+\n",
      "|artistid| userid|plays|     name|\n",
      "+--------+-------+-----+---------+\n",
      "| 1000010|1000002|  314|Aerosmith|\n",
      "| 1000010|1000019|   11|Aerosmith|\n",
      "| 1000010|1000022|   78|Aerosmith|\n",
      "| 1000010|1000025|   50|Aerosmith|\n",
      "| 1000010|1000028|   29|Aerosmith|\n",
      "| 1000010|1000060|    6|Aerosmith|\n",
      "| 1000010|1000071|    9|Aerosmith|\n",
      "| 1000010|1000092|    8|Aerosmith|\n",
      "| 1000010|1000098|    7|Aerosmith|\n",
      "| 1000010|1000180|   25|Aerosmith|\n",
      "| 1000010|1000239|   13|Aerosmith|\n",
      "| 1000010|1000241|   17|Aerosmith|\n",
      "| 1000010|1000246|   10|Aerosmith|\n",
      "| 1000010|1000288|   41|Aerosmith|\n",
      "| 1000010|1000326|   10|Aerosmith|\n",
      "| 1000010|1000346|   29|Aerosmith|\n",
      "| 1000010|1000356|   22|Aerosmith|\n",
      "| 1000010|1000376|    5|Aerosmith|\n",
      "| 1000010|1000471|    9|Aerosmith|\n",
      "| 1000010|1000509|   17|Aerosmith|\n",
      "+--------+-------+-----+---------+\n",
      "only showing top 20 rows\n",
      "\n",
      "+--------+------+-----+----+\n",
      "|artistid|userid|plays|name|\n",
      "+--------+------+-----+----+\n",
      "+--------+------+-----+----+\n",
      "\n"
     ]
    }
   ],
   "source": [
    "artists[artists.artistid==1000010].show()\n",
    "artists[artists.artistid==2082323].show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+-------+------------------+------------------+-----------------+--------------+\n",
      "|summary|          artistid|            userid|            plays|          name|\n",
      "+-------+------------------+------------------+-----------------+--------------+\n",
      "|  count|           9306376|           9306376|          9306376|       9306168|\n",
      "|   mean|1319277.8503197164|1939115.8133319565|33.87218537054596|      Infinity|\n",
      "| stddev| 2015061.840234866|490788.62697331404|66.61000116270195|           NaN|\n",
      "|    min|                 1|                90|                5|            \u0019Z|\n",
      "|    max|          10794235|           2443507|             1000|￾氀挀 渀䌀愀渀|\n",
      "+-------+------------------+------------------+-----------------+--------------+\n",
      "\n"
     ]
    }
   ],
   "source": [
    "subset = artists\n",
    "subset.describe().show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "root\n",
      " |-- artistid: integer (nullable = true)\n",
      " |-- userid: integer (nullable = true)\n",
      " |-- plays: integer (nullable = true)\n",
      " |-- name: string (nullable = true)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "subset.printSchema()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.5 转换plays数据列\n",
    "    由2.3中数据分布直方图可知，在数据集user_artist_data.txt中，plays字段的存在明显向右偏趋势，因此我们尝试对plays数据列取对数操作从而将plays字段进行标准化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "# plays数据列取对数后保存到新的log_plays数据列中\n",
    "subset = subset.withColumn(\"log_plays\",log1p(\"plays\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 从subset数据集中随机采样\n",
    "subset_pd = subset.sample(withReplacement=False, fraction=0.01, seed=42).toPandas()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, 'Play Count Distribution')"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1080x432 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 查看随机采样数据分布情况\n",
    "plt.subplots(figsize=(15, 6))\n",
    "sns.distplot(subset_pd.log_plays)\n",
    "plt.grid(which=\"major\", axis=\"y\")\n",
    "plt.xlabel(\"Play Count\", size=14)\n",
    "plt.title(\"Play Count Distribution\", size=16)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.6 拆分数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "train,test = subset.randomSplit([0.8, 0.2], seed=42)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 三、模型训练\n",
    "#### 3.1 ALS算法简介\n",
    "    LS中文名作交替最小二乘法，在机器学习中，ALS特指使用最小二乘法求解的一个协同过滤算法，是协同过滤中的一种。ALS算法是2008年以来，用的比较多的协同过滤算法。它已经集成到Spark的Mllib库中，使用起来比较方便。从协同过滤的分类来说，ALS算法属于User-Item CF，也叫做混合CF，因为它同时考虑了User和Item两个方面，即即可基于用户进行推荐又可基于物品进行推推荐\n",
    "    一般而言用户只会购买物品集中的极少数部分产品，并对其进行打分。考虑下面这样一个包含用户的打分矩阵（列为用户u1-u6，行为物品I1-I8），我们可以看到这个用户的评分矩阵是十分稀疏的，有很多用户的购买的记录是空的，而且在现实业务中，用户的评分矩阵会更加的稀疏。如何通过这样一个稀疏矩阵，对用户进行协同推荐用户可能很喜欢的物品对于推荐系统而言是一种很大的考验。\n",
    "![image.png](data/musicRecommand/1.png)\n",
    "\n",
    "    在spark MLlib 机器学习库中目前推荐模型只包含基于矩阵分解(matrix factorization)的实现。具体的分解思路，找出两个低维的矩阵，使得它们的乘积是原始矩阵。因此这也是一种降维技术。假设我们的用户和物品分别是U和I，那对应的“用户-物品”矩阵的维度为U×I，类似图一所示：\n",
    "![image.png](data/musicRecommand/2.png)\n",
    "\n",
    "    上图中找到和“用户-物品“矩阵近似的k维（低阶）矩阵，最终还是要求出如下两个矩阵：一个用于表示用户U×k维矩阵，以及一个表征物品的I×k维矩阵。这两个矩阵也称为因子矩阵，他们的矩阵乘积便是原始评级数据的一个近似值。值得注意的是，原始评级矩阵通常很稀疏，但因子矩阵却是稠密的，如图二所示：\n",
    "![image.png](data/musicRecommand/3.png)\n",
    "\n",
    "    ALS是求解矩阵分解问题的一种最优化方法，它功能强大，效果理想而且被证明相对容易实现。这使得它很适合如Spark这样的平台。\n",
    "\n",
    "    ALS的实现原理是迭代式求解一系列最小二乘回归问题。在每次迭代时，固定用户因子矩阵或者是物品因子矩阵中的一个，然后用固定的这个矩阵以及评级数据来更新另一个矩阵。之后，被更新的矩阵被固定住，再更新另外一个矩阵。如此迭代，知道模型收敛（或者是迭代了预设好的次数）。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入模型训练与模型评估模块\n",
    "from pyspark.ml.recommendation import ALS\n",
    "from pyspark.ml.evaluation import RegressionEvaluator\n",
    "from pyspark.mllib.evaluation import BinaryClassificationMetrics"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建模型实例对象\n",
    "als = ALS(maxIter=5, \n",
    "          regParam=0.01, \n",
    "          userCol='userid', \n",
    "          itemCol='artistid', \n",
    "          ratingCol='log_plays')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 3.2 ALS算法参数\n",
    "+ maxIter 模型最大迭代次数\n",
    "+ userCol 用户id数据列名称\n",
    "+ itemCol 物品id数据列名称\n",
    "+ ratingCol 用户对物品评分数据列名称\n",
    "\n",
    "注意以上模型初始化时，针对用户对物品的评分数据，我们选择取对数后的数据列log_plays而不是原始的plays数据列"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "model = als.fit(train)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 四、模型评估"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 4.1 使用训练好的模型对测试及做预测"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "predictions = model.transform(test)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "predictions = predictions.dropna(subset=('prediction'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+--------+-------+-----+--------------------+------------------+----------+\n",
      "|artistid| userid|plays|                name|         log_plays|prediction|\n",
      "+--------+-------+-----+--------------------+------------------+----------+\n",
      "|      22|1026623|   90| Nocturnal Emissions|  4.51085950651685| 3.3683236|\n",
      "|      22|1031888|   64| Nocturnal Emissions| 4.174387269895637| 4.9404078|\n",
      "|      22|2059316|   75| Nocturnal Emissions| 4.330733340286331|  3.395858|\n",
      "|      22|1031898|    5| Nocturnal Emissions| 1.791759469228055| 3.6090603|\n",
      "|      22|2118415|   11| Nocturnal Emissions|2.4849066497880004| 3.9833508|\n",
      "|      23|2213999|   18|Utah Philips & An...|2.9444389791664403| 2.6726527|\n",
      "|      24|1003690|    7|            Zap Mama|2.0794415416798357| 2.9603093|\n",
      "|      24|1040821|   50|            Zap Mama|3.9318256327243257|  2.965948|\n",
      "|      24|2013538|   10|            Zap Mama|2.3978952727983707| 3.2170956|\n",
      "|      24|2046114|    7|            Zap Mama|2.0794415416798357| 2.6985645|\n",
      "|      24|2070889|   69|            Zap Mama| 4.248495242049359| 2.4116156|\n",
      "|      24|2073444|    9|            Zap Mama| 2.302585092994046| 2.6254153|\n",
      "|      24|2111872|    6|            Zap Mama|1.9459101490553132| 2.9088213|\n",
      "|      24|2161992|   16|            Zap Mama| 2.833213344056216| 1.9029834|\n",
      "|      24|2166013|   25|            Zap Mama| 3.258096538021482|   3.27588|\n",
      "|      24|2212259|    5|            Zap Mama| 1.791759469228055| 2.7063646|\n",
      "|      24|2227292|   14|            Zap Mama|  2.70805020110221| 3.1805153|\n",
      "|      24|2239284|   28|            Zap Mama| 3.367295829986474| 2.6900468|\n",
      "|      24|2256036|    5|            Zap Mama| 1.791759469228055| 2.9605863|\n",
      "|      24|2276285|    5|            Zap Mama| 1.791759469228055| 3.1597173|\n",
      "+--------+-------+-----+--------------------+------------------+----------+\n",
      "only showing top 20 rows\n",
      "\n"
     ]
    }
   ],
   "source": [
    "predictions.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 4.2 分别使用三种指标对模型做评估\n",
    "+ RMSE 均方根误差\n",
    "![image](data/musicRecommand/4.gif)\n",
    "+ log RMSE 对数均方根误差\n",
    "    + 在RMSE基础上取对数即可\n",
    "+ R 方值\n",
    "![image](data/musicRecommand/5.gif)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [],
   "source": [
    "evaluator_rmse = RegressionEvaluator(metricName='rmse',\n",
    "                                     labelCol='log_plays', \n",
    "                                     predictionCol='prediction')\n",
    "rmse = evaluator_rmse.evaluate(predictions.na.drop())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [],
   "source": [
    "evaluator_r2 = RegressionEvaluator(metricName='r2', \n",
    "                                   labelCol='log_plays', \n",
    "                                   predictionCol='prediction')\n",
    "r2 = evaluator_r2.evaluate(predictions.na.drop())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "RMSE (Log)\n",
      "0.9629073801640379\n",
      "\n",
      "RMSE\n",
      "1.6193007168011497\n",
      "\n",
      "R Squared\n",
      "0.02692563321964092\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print('RMSE (Log)')\n",
    "print(rmse)\n",
    "print()\n",
    "print('RMSE')\n",
    "print(np.expm1(rmse))\n",
    "print()\n",
    "print('R Squared')\n",
    "print(r2)\n",
    "print()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 五、模型参数优化\n",
    "    使用spark MLlib ALS算法构建模型，模型训练时我们主要需要考虑以下几个参数的选择\n",
    "+ rank： 模型中的隐藏因子数目，即3.2小节用户特征矩阵与物品矩阵中的k值\n",
    "+ iteration： 模型迭代优化的次数，如果让模型一直迭代，直到模型收敛，将有可能需要很长时间。因此，我们一般会设置一个最大的迭代次数\n",
    "+ lambda： ALS正则化系数，将该参数设置较大可防止模型过拟合，但是太大将导致模型准确率降低\n",
    "\n",
    "接下来我们将通过spak MLlib中的CrossValidator和ParamGridBuilder进行参数的网格搜索，从而寻找出最佳的参数组合"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 5.1 GridSearch参数网格搜索"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pyspark.ml.tuning import CrossValidator, ParamGridBuilder"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 设置参数组合，以下一共设置了 4 x 1 x 4 共16种参数组合\n",
    "paramGrid = ParamGridBuilder(). \\\n",
    "            addGrid(als.rank, [1, 5, 10, 20]). \\\n",
    "            addGrid(als.maxIter, [20]). \\\n",
    "            addGrid(als.regParam, [0.001, 0.005, 0.01, 0.1]).build()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 使用交叉验证方式，构建网格搜索模型\n",
    "als_cv = ALS(userCol='userid', \n",
    "             itemCol='artistid', \n",
    "             ratingCol='log_plays')\n",
    "\n",
    "crossval = CrossValidator(estimator=als_cv, \n",
    "                          estimatorParamMaps=paramGrid, \n",
    "                          evaluator=evaluator_rmse, \n",
    "                          numFolds=3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 开始训练模型，注意，以下代码共需要训练16个模型，训练时间较长\n",
    "cvModel = crossval.fit(train)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 5.2 模型预测和评估\n",
    "    使用网格搜索构建的模型对测试集做预测及模型评估"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [],
   "source": [
    "predictions_cv = cvModel.transform(test)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [],
   "source": [
    "rmse_cv = evaluator_rmse.evaluate(predictions_cv.na.drop())\n",
    "r2_cv = evaluator_r2.evaluate(predictions_cv.na.drop())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+--------+-------+-----+--------------------+------------------+----------+\n",
      "|artistid| userid|plays|                name|         log_plays|prediction|\n",
      "+--------+-------+-----+--------------------+------------------+----------+\n",
      "|      22|1026623|   90| Nocturnal Emissions|  4.51085950651685| 2.9419727|\n",
      "|      22|1031888|   64| Nocturnal Emissions| 4.174387269895637| 4.5687976|\n",
      "|      22|2059316|   75| Nocturnal Emissions| 4.330733340286331| 2.6772575|\n",
      "|      22|1031898|    5| Nocturnal Emissions| 1.791759469228055|  3.362953|\n",
      "|      22|2118415|   11| Nocturnal Emissions|2.4849066497880004| 2.8072913|\n",
      "|      23|2213999|   18|Utah Philips & An...|2.9444389791664403|  2.354411|\n",
      "|      24|1003690|    7|            Zap Mama|2.0794415416798357| 2.9051342|\n",
      "|      24|1040821|   50|            Zap Mama|3.9318256327243257| 2.8204834|\n",
      "|      24|2013538|   10|            Zap Mama|2.3978952727983707|  3.111197|\n",
      "|      24|2046114|    7|            Zap Mama|2.0794415416798357| 2.4793422|\n",
      "|      24|2070889|   69|            Zap Mama| 4.248495242049359| 2.8588173|\n",
      "|      24|2073444|    9|            Zap Mama| 2.302585092994046| 2.7812662|\n",
      "|      24|2111872|    6|            Zap Mama|1.9459101490553132| 2.5694106|\n",
      "|      24|2161992|   16|            Zap Mama| 2.833213344056216| 2.1164134|\n",
      "|      24|2166013|   25|            Zap Mama| 3.258096538021482| 3.0163977|\n",
      "|      24|2212259|    5|            Zap Mama| 1.791759469228055| 2.7693648|\n",
      "|      24|2227292|   14|            Zap Mama|  2.70805020110221|  2.749824|\n",
      "|      24|2239284|   28|            Zap Mama| 3.367295829986474|  2.935473|\n",
      "|      24|2256036|    5|            Zap Mama| 1.791759469228055|  2.642371|\n",
      "|      24|2276285|    5|            Zap Mama| 1.791759469228055| 2.9637165|\n",
      "+--------+-------+-----+--------------------+------------------+----------+\n",
      "only showing top 20 rows\n",
      "\n"
     ]
    }
   ],
   "source": [
    "predictions_cv.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "RMSE (Log)\n",
      "0.8644174334388961\n",
      "\n",
      "RMSE\n",
      "1.373622889790046\n",
      "\n",
      "R Squared\n",
      "0.21580505987238086\n"
     ]
    }
   ],
   "source": [
    "print('RMSE (Log)')\n",
    "print(rmse_cv)\n",
    "print()\n",
    "print('RMSE')\n",
    "print(np.expm1(rmse_cv))\n",
    "print()\n",
    "print('R Squared')\n",
    "print(r2_cv)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 5.3 寻找最佳参数组合\n",
    "    {maxIter: 20, regParam: 0.001, rank: 1}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"estimator: estimator to be cross-validated (current: ALS_f2b75929a0c4)\\nestimatorParamMaps: estimator param maps (current: [{Param(parent='ALS_66731c3cd333', name='rank', doc='rank of the factorization'): 1, Param(parent='ALS_66731c3cd333', name='maxIter', doc='max number of iterations (>= 0).'): 20, Param(parent='ALS_66731c3cd333', name='regParam', doc='regularization parameter (>= 0).'): 0.001}, {Param(parent='ALS_66731c3cd333', name='rank', doc='rank of the factorization'): 1, Param(parent='ALS_66731c3cd333', name='maxIter', doc='max number of iterations (>= 0).'): 20, Param(parent='ALS_66731c3cd333', name='regParam', doc='regularization parameter (>= 0).'): 0.005}, {Param(parent='ALS_66731c3cd333', name='rank', doc='rank of the factorization'): 1, Param(parent='ALS_66731c3cd333', name='maxIter', doc='max number of iterations (>= 0).'): 20, Param(parent='ALS_66731c3cd333', name='regParam', doc='regularization parameter (>= 0).'): 0.01}, {Param(parent='ALS_66731c3cd333', name='rank', doc='rank of the factorization'): 1, Param(parent='ALS_66731c3cd333', name='maxIter', doc='max number of iterations (>= 0).'): 20, Param(parent='ALS_66731c3cd333', name='regParam', doc='regularization parameter (>= 0).'): 0.1}, {Param(parent='ALS_66731c3cd333', name='rank', doc='rank of the factorization'): 5, Param(parent='ALS_66731c3cd333', name='maxIter', doc='max number of iterations (>= 0).'): 20, Param(parent='ALS_66731c3cd333', name='regParam', doc='regularization parameter (>= 0).'): 0.001}, {Param(parent='ALS_66731c3cd333', name='rank', doc='rank of the factorization'): 5, Param(parent='ALS_66731c3cd333', name='maxIter', doc='max number of iterations (>= 0).'): 20, Param(parent='ALS_66731c3cd333', name='regParam', doc='regularization parameter (>= 0).'): 0.005}, {Param(parent='ALS_66731c3cd333', name='rank', doc='rank of the factorization'): 5, Param(parent='ALS_66731c3cd333', name='maxIter', doc='max number of iterations (>= 0).'): 20, Param(parent='ALS_66731c3cd333', name='regParam', doc='regularization parameter (>= 0).'): 0.01}, {Param(parent='ALS_66731c3cd333', name='rank', doc='rank of the factorization'): 5, Param(parent='ALS_66731c3cd333', name='maxIter', doc='max number of iterations (>= 0).'): 20, Param(parent='ALS_66731c3cd333', name='regParam', doc='regularization parameter (>= 0).'): 0.1}, {Param(parent='ALS_66731c3cd333', name='rank', doc='rank of the factorization'): 10, Param(parent='ALS_66731c3cd333', name='maxIter', doc='max number of iterations (>= 0).'): 20, Param(parent='ALS_66731c3cd333', name='regParam', doc='regularization parameter (>= 0).'): 0.001}, {Param(parent='ALS_66731c3cd333', name='rank', doc='rank of the factorization'): 10, Param(parent='ALS_66731c3cd333', name='maxIter', doc='max number of iterations (>= 0).'): 20, Param(parent='ALS_66731c3cd333', name='regParam', doc='regularization parameter (>= 0).'): 0.005}, {Param(parent='ALS_66731c3cd333', name='rank', doc='rank of the factorization'): 10, Param(parent='ALS_66731c3cd333', name='maxIter', doc='max number of iterations (>= 0).'): 20, Param(parent='ALS_66731c3cd333', name='regParam', doc='regularization parameter (>= 0).'): 0.01}, {Param(parent='ALS_66731c3cd333', name='rank', doc='rank of the factorization'): 10, Param(parent='ALS_66731c3cd333', name='maxIter', doc='max number of iterations (>= 0).'): 20, Param(parent='ALS_66731c3cd333', name='regParam', doc='regularization parameter (>= 0).'): 0.1}, {Param(parent='ALS_66731c3cd333', name='rank', doc='rank of the factorization'): 20, Param(parent='ALS_66731c3cd333', name='maxIter', doc='max number of iterations (>= 0).'): 20, Param(parent='ALS_66731c3cd333', name='regParam', doc='regularization parameter (>= 0).'): 0.001}, {Param(parent='ALS_66731c3cd333', name='rank', doc='rank of the factorization'): 20, Param(parent='ALS_66731c3cd333', name='maxIter', doc='max number of iterations (>= 0).'): 20, Param(parent='ALS_66731c3cd333', name='regParam', doc='regularization parameter (>= 0).'): 0.005}, {Param(parent='ALS_66731c3cd333', name='rank', doc='rank of the factorization'): 20, Param(parent='ALS_66731c3cd333', name='maxIter', doc='max number of iterations (>= 0).'): 20, Param(parent='ALS_66731c3cd333', name='regParam', doc='regularization parameter (>= 0).'): 0.01}, {Param(parent='ALS_66731c3cd333', name='rank', doc='rank of the factorization'): 20, Param(parent='ALS_66731c3cd333', name='maxIter', doc='max number of iterations (>= 0).'): 20, Param(parent='ALS_66731c3cd333', name='regParam', doc='regularization parameter (>= 0).'): 0.1}])\\nevaluator: evaluator used to select hyper-parameters that maximize the validator metric (current: RegressionEvaluator_1b227f966008)\\nseed: random seed. (default: 4523670370584151748)\""
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cvModel.explainParams()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 六、使用模型做推荐\n",
    "    接下来，我们将使用模型对一些用户做推荐并对推荐结果做分析\n",
    "#### 6.1 为每个用户和艺术家做排名前10的推荐\n",
    "+ 针对每个用户，推荐其最喜欢的前10名艺术家\n",
    "+ 针对每个艺术家，推荐最喜欢他的前10名用户"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Generate top 10 movie recommendations for each user\n",
    "userRecs = model.recommendForAllUsers(10)\n",
    "# Generate top 10 user recommendations for each artist\n",
    "artistRecs = model.recommendForAllItems(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Row(userid=90, recommendations=[Row(artistid=1089698, rating=17.40837287902832), Row(artistid=1285799, rating=17.00878143310547), Row(artistid=2154769, rating=16.24146842956543), Row(artistid=6810092, rating=16.051210403442383), Row(artistid=1191252, rating=15.527922630310059), Row(artistid=1216503, rating=15.405241012573242), Row(artistid=1305242, rating=14.921867370605469), Row(artistid=1080933, rating=14.910660743713379), Row(artistid=1268927, rating=14.836357116699219), Row(artistid=6607132, rating=14.823577880859375)])"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "userRecs.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+--------+--------------------+\n",
      "|artistid|     recommendations|\n",
      "+--------+--------------------+\n",
      "|      22|[[2418759, 22.981...|\n",
      "|      23|[[2356456, 7.9399...|\n",
      "|      24|[[2377404, 10.333...|\n",
      "|      32|[[2356456, 9.7765...|\n",
      "|      35|[[2146606, 12.892...|\n",
      "|      39|[[2109736, 8.6978...|\n",
      "|      41|[[2275264, 9.3344...|\n",
      "|      48|[[2363312, 10.509...|\n",
      "|      53|[[2377404, 8.0104...|\n",
      "|      61|[[2405723, 11.481...|\n",
      "|      69|[[2117710, 11.063...|\n",
      "|      71|[[2275264, 8.9834...|\n",
      "|      90|[[2356456, 10.629...|\n",
      "|      92|[[1002266, 12.412...|\n",
      "|      95|[[2377404, 9.0959...|\n",
      "|     106|[[2109736, 6.7616...|\n",
      "|     123|[[2138643, 9.0405...|\n",
      "|     128|[[2131182, 17.100...|\n",
      "|     139|[[2394290, 19.616...|\n",
      "|     144|[[2252743, 10.795...|\n",
      "+--------+--------------------+\n",
      "only showing top 20 rows\n",
      "\n"
     ]
    }
   ],
   "source": [
    "artistRecs.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 6.2 统计播放两最多的前10名艺术家"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, 'Top-10 Artists per play counts')"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1080x432 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "top10ArtistsPC = subset.groupBy(\"name\").sum(\"plays\").orderBy('sum(plays)', ascending=0).take(10)\n",
    "top10ArtistsPC = pd.DataFrame(data=top10ArtistsPC)\n",
    "\n",
    "plt.subplots(figsize=(15, 6))\n",
    "sns.barplot(x=top10ArtistsPC[1]/1000000, y=top10ArtistsPC[0])\n",
    "plt.xlabel('Play Count  (in millions)')\n",
    "plt.ylabel('Artist')\n",
    "plt.title('Top-10 Artists per play counts')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 6.3 根据用户数量，统计最受欢迎的前10名艺术家"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, 'Top-10 Artists per audience size')"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1080x432 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "top10ArtistsUC = (subset.dropDuplicates(['userid', 'artistid']). \\\n",
    "                  groupBy(\"name\"). \\\n",
    "                  count(). \\\n",
    "                  orderBy(\"count\", ascending=0). \\\n",
    "                  take(10))\n",
    "top10ArtistsUC = pd.DataFrame(top10ArtistsUC)\n",
    "\n",
    "plt.subplots(figsize=(15, 6))\n",
    "sns.barplot(x=top10ArtistsPC[1]/1000000, y=top10ArtistsUC[0])\n",
    "plt.xlabel('Audience size (in millions)')\n",
    "plt.ylabel('Artist')\n",
    "plt.title('Top-10 Artists per audience size')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 6.4 统计播放次数最多的前10名用户"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, 'Top-10 Users per play counts')"
      ]
     },
     "execution_count": 58,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1080x432 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# calculate top 10 users interm of play counts\n",
    "top10UsersByPlayCount = subset.groupBy(\"userid\").sum(\"plays\").orderBy('sum(plays)', ascending=0).take(10)\n",
    "top10UsersByPlayCount = pd.DataFrame(top10UsersByPlayCount)\n",
    "top10UsersByPlayCount[0] = top10UsersByPlayCount[0].astype(str)\n",
    "topusers = top10UsersByPlayCount[0].astype(str)\n",
    "top10UsersByPlayCount[0] = pd.DataFrame(topusers)\n",
    "top10UsersByPlayCount.set_index(topusers.sort_values(ascending=True), inplace=True)\n",
    "top10UsersByPlayCount.drop(columns=0)\n",
    "\n",
    "top10UsersByPlayCount.plot(kind='barh', figsize=(15,6))\n",
    "plt.xlabel('Play Count')\n",
    "plt.ylabel('User')\n",
    "plt.title('Top-10 Users per play counts')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 6.5 使用模型对单个用户做推荐\n",
    "    首先，在使用模型做推荐之前，我们会将user_artist_data.txt与artist_data.txt数据集进行链接，方便我们查看艺术家id和名称，而不是仅仅看到艺术家id，同时，我们也会将一些id重复的艺术家数据过滤掉。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "root\n",
      " |-- artistid: integer (nullable = true)\n",
      " |-- userid: integer (nullable = true)\n",
      " |-- plays: integer (nullable = true)\n",
      " |-- name: string (nullable = true)\n",
      " |-- log_plays: double (nullable = true)\n",
      " |-- prediction: float (nullable = false)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "predictions.printSchema()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "root\n",
      " |-- artistid: integer (nullable = true)\n",
      " |-- userid: integer (nullable = true)\n",
      " |-- plays: integer (nullable = true)\n",
      " |-- name: string (nullable = true)\n",
      " |-- log_plays: double (nullable = true)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "subset.printSchema()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [],
   "source": [
    "predictions = predictions.join(subset.drop(subset['userid']). \\\n",
    "                          drop(subset['plays']). \\\n",
    "                          drop(subset['name']), on=\"artistid\"). \\\n",
    "                          dropDuplicates()\n",
    "\n",
    "predictions = predictions.withColumn('scaled_prediction', \n",
    "                                     (predictions[\"prediction\"]+9.208462)/(15.73186+9.208462))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "创建一个函数，用于显示为任意用户推荐的最佳结果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [],
   "source": [
    "def user_predictions(user):\n",
    "    single_user = predictions.filter(test[\"userid\"] == user).select(\n",
    "        [\"userid\", \"name\", \"scaled_prediction\"]\n",
    "    )\n",
    "    single_user.withColumn(\"artist\", (single_user[\"name\"])).drop(\n",
    "        single_user[\"name\"]\n",
    "    ).orderBy(\"scaled_prediction\", ascending=False).show(truncate=False)\n",
    "\n",
    "options = [1024631, 1026623, 1000002, 1071243, 1026623, 1071243, \n",
    "           2091925, 2287497, 2327206, 1005157, 2060053, 1024631, 1026623, \n",
    "           1000002, 1071243, 1026623, 1071243, 2091925, 2287497, 2327206, 1005157]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "8ec39cb1b8b34f0c94fe0d05c154ab8f",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "interactive(children=(Dropdown(description='user', options=(1024631, 1026623, 1000002, 1071243, 1026623, 10712…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<function __main__.user_predictions(user)>"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "interact(user_predictions, user=options)\n",
    "#user_predictions(1024631)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
